/*
* Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* $Id: sessiontabwidget.cpp 587 2009-05-19 20:19:17Z jpnurmi $
*/

#include "sessiontabwidget.h"
#include "sessionmodelitem.h"
#include "completionmodel.h"
#include "commandengine.h"
#include "sessionmodel.h"
#include "application.h"
#include "messageview.h"
#include "session.h"
#include <ircutil.h>
#include <irc.h>
#include <QtGui>

SessionTabWidget::SessionTabWidget(Session* session, QWidget* parent) :
    TabWidget(parent), model(0), sessio(session), engine(0)
{
    QIcon icon(":/resources/oxygen/16x16/actions/bookmark.png");
    setAlertIcon(icon);
    setHighlightIcon(icon);

    connect(this, SIGNAL(currentChanged(int)), this, SLOT(tabActivated(int)));

    QShortcut* shortcut = new QShortcut(QKeySequence::Close, this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(closeCurrentView()));

    shortcut = new QShortcut(QKeySequence(tr("Alt+Left")), this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(moveToPrevTab()));

    shortcut = new QShortcut(QKeySequence(tr("Alt+Right")), this);
    connect(shortcut, SIGNAL(activated()), this, SLOT(moveToNextTab()));

    model = new SessionModel(this);

    engine = new CommandEngine(this);
    engine->setScriptObject("app", qApp);
    engine->setScriptObject("window", window());
    engine->setScriptObject("session", session);
    engine->setScriptObject("tabWidget", this);
}

Session* SessionTabWidget::session() const
{
    return sessio;
}

QStringList SessionTabWidget::channels() const
{
    // TODO:
    //return handler.channels();
    return QStringList();
}

void SessionTabWidget::openView(const QString& receiver)
{
    /*
    MessageView* view = 0;
    if ((view = handler.view(receiver)))
    {
        setCurrentWidget(view);
    }
    else
    {
        view = new MessageView(receiver, this);
        connect(view, SIGNAL(send(QString)), this, SLOT(send(QString)));
        connect(view, SIGNAL(query(QString)), this, SLOT(openView(QString)));
        connect(view, SIGNAL(command(QString)), sessio, SLOT(sendRaw(QString)));

        handler.addView(view);
        int index = addTab(view, receiver);
        setCurrentIndex(index);

        CompletionModel* proxyModel = new CompletionModel(view);
        QList<QStandardItem*> items = model->findItems(receiver, Qt::MatchFixedString);
        proxyModel->setCompletionModel(model, items.value(0));
        view->setCompletionModel(proxyModel);
    }
    */
}

void SessionTabWidget::closeCurrentView(const QString& message)
{
    if (count() > 1)
    {
        int index = currentIndex();
        QString receiver = tabText(index);
        closeView(receiver, message);
    }
}

void SessionTabWidget::closeView(const QString& receiver, const QString& message)
{
    /*
    QString tmp = receiver;
    if (!tmp.startsWith('#') && !tmp.startsWith('&') && !handler.contains(tmp))
        tmp.prepend("#");

    if (tmp.startsWith('#') || tmp.startsWith('&'))
        sessio->cmdPart(tmp, message);

    MessageView* view = handler.takeView(tmp);
    if (view)
    {
        if (indexOf(view) == 0)
        {
            // closing a server tab
            emit disconnectFrom(QString());
            deleteLater();
        }
        view->deleteLater();
    }
    */
}

void SessionTabWidget::showHelp(const QString& text)
{
    MessageView* view = static_cast<MessageView*>(currentWidget());
    if (text.isNull())
    {
        QMapIterator<QString, Command> it(Application::commands());
        while (it.hasNext())
        {
            Command command = it.next().value();
            QStringList params;
            params << command.name.toUpper() 
                   << Irc::Util::messageToHtml(command.help) 
                   << Irc::Util::messageToHtml(command.description);
            view->receiveMessage(tr("? %1 %2 - %3"), params);
        }
    }
    else
    {
        Command command = Application::commands().value(text);
        QStringList params;
        params << command.name.toUpper() 
               << Irc::Util::messageToHtml(command.help) 
               << Irc::Util::messageToHtml(command.description);
        view->receiveMessage(tr("? %1 %2 - %3"), params);
    }
}

/*
void SessionTabWidget::on_msgJoined(const QString& origin, const QString& channel)
{
    model->joined(origin, channel);

    QString target = sessio->resolveTarget(origin, channel);
    prepareView(target, true, Application::settings().highlights.value(Settings::Joins));
    foreach (MessageView* view, handler.findViews(QStringList() << origin << channel))
    {
        if (Application::settings().messages.value(Settings::Joins))
        {
            QStringList params;
            params << view->senderHtml(origin) << channel;
            view->receiveMessage(tr("! %1 joined %2"), params);
        }
    }
}

void SessionTabWidget::on_msgParted(const QString& origin, const QString& channel, const QString& message)
{
    const bool own = origin.compare(sessio->nick(), Qt::CaseInsensitive) == 0;
    foreach (MessageView* view, handler.findViews(QStringList() << origin << channel))
    {
        if (own)
        {
            handler.removeView(view);
        }
        else if (Application::settings().messages.value(Settings::Parts))
        {
            QString target = sessio->resolveTarget(origin, view->receiver());
            prepareView(target, false, Application::settings().highlights.value(Settings::Parts));

            QStringList params;
            params << view->senderHtml(origin) << channel;
            if (!message.isEmpty())
                view->receiveMessage(tr("! %1 parted %2 (%3)"), params << message);
            else
                view->receiveMessage(tr("! %1 parted %2"), params);
        }
    }
    model->parted(origin, channel, own);
}

void SessionTabWidget::on_msgQuit(const QString& origin, const QString& message)
{
    foreach (MessageView* view, handler.findViews(QStringList() << origin, QStringList() << origin))
    {
        if (Application::settings().messages.value(Settings::Quits))
        {
            QString target = sessio->resolveTarget(origin, view->receiver());
            prepareView(target, false, Application::settings().highlights.value(Settings::Quits));

            QStringList params;
            params << view->prefixedSender(origin);
            if (!message.isEmpty())
                view->receiveMessage(tr("! %1 has quit (%2)"), params << message);
            else
                view->receiveMessage(tr("! %1 has quit"), params);
        }
    }
    model->quit(origin);
}

void SessionTabWidget::on_msgNickChanged(const QString& origin, const QString& nick)
{
    const bool own = origin.compare(sessio->nick(), Qt::CaseInsensitive) == 0;
    foreach (MessageView* view, own ? handler.views() : handler.findViews(QStringList() << origin, QStringList() << origin))
    {
        if (Application::settings().messages.value(Settings::Nicks))
        {
            QString target = sessio->resolveTarget(origin, view->receiver());
            prepareView(target, false, Application::settings().highlights.value(Settings::Nicks));

            QStringList params;
            params << view->prefixedSender(origin) << view->senderHtml(nick);
            view->receiveMessage(tr("! %1 changed nick to %2"), params);
        }
        if (view->matches(origin))
        {
            handler.renameView(view->receiver(), nick);
            setTabText(indexOf(view), nick);
        }
    }
    model->nickChanged(origin, nick);
}

void SessionTabWidget::on_msgModeChanged(const QString& origin, const QString& receiver, const QString& mode, const QString& args)
{
    QString target = sessio->resolveTarget(origin, receiver);
    prepareView(target, true, Application::settings().highlights.value(Settings::Modes));

    if (Application::settings().messages.value(Settings::Modes))
    {
        QStringList params;
        MessageView* view = handler.view(target);
        params << view->senderHtml(origin) << mode << args;
        view->receiveMessage(tr("! %1 sets mode %2 %3"), params);
    }
    model->modeChanged(receiver, mode, args);
}

void SessionTabWidget::on_msgTopicChanged(const QString& origin, const QString& channel, const QString& topic)
{
    QString target = sessio->resolveTarget(origin, channel);
    prepareView(target, true, Application::settings().highlights.value(Settings::Topics));

    if (Application::settings().messages.value(Settings::Topics))
    {
        QStringList params;
        MessageView* view = handler.view(target);
        params << view->senderHtml(origin) << Irc::Util::messageToHtml(topic) << channel;
        view->receiveMessage(tr("! %1 sets topic \"%2\" on %3"), params);
    }
}

void SessionTabWidget::on_msgInvited(const QString& origin, const QString& receiver, const QString& channel)
{
    Q_UNUSED(receiver);
    QStringList params;
    MessageView* view = static_cast<MessageView*>(currentWidget());
    params << view->senderHtml(origin) << view->senderHtml(channel);
    view->receiveMessage(tr("! %1 invited to %3"), params);
}

void SessionTabWidget::on_msgKicked(const QString& origin, const QString& channel, const QString& nick, const QString& message)
{
    QString target = sessio->resolveTarget(origin, channel);
    prepareView(target, true, Application::settings().highlights.value(Settings::Kicks));

    if (Application::settings().messages.value(Settings::Kicks))
    {
        QStringList params;
        MessageView* view = handler.view(target);
        params << view->senderHtml(origin) << view->senderHtml(nick);
        if (!message.isEmpty())
            view->receiveMessage(tr("! %1 kicked %2 (%3)"), params << message);
        else
            view->receiveMessage(tr("! %1 kicked %2"), params);
    }
    model->kicked(channel, nick);
}

void SessionTabWidget::on_msgCtcpRequestReceived(const QString& origin, const QString& request)
{
    MessageView* view = static_cast<MessageView*>(currentWidget());

    QStringList params;
    params << view->senderHtml(origin) << request.split(QRegExp("\\s")).first().toUpper();
    view->receiveMessage(tr("! %1 requested CTCP-%2"), params);

    if (!request.compare("VERSION", Qt::CaseInsensitive))
        sessio->cmdCtcpReply(origin, QString("%1 %2 %3").arg(request).arg(qApp->applicationName()).arg(qApp->applicationVersion()));
    else if (request.startsWith("PING", Qt::CaseInsensitive))
        sessio->cmdCtcpReply(origin, request);
    else if (request.startsWith("TIME", Qt::CaseInsensitive))
        sessio->cmdCtcpReply(origin, QString("%1 %2").arg(request).arg(QDateTime::currentDateTime().toString()));
    else
        qFatal("SessionTabWidget::on_ctcpRequestReceived '%s' requested '%s'", qPrintable(origin), qPrintable(request));
}

void SessionTabWidget::on_msgCtcpReplyReceived(const QString& origin, const QString& reply)
{
    QString message = reply;
    if (message.startsWith("PING", Qt::CaseInsensitive))
    {
        QStringList params = message.split(QRegExp("\\s"), QString::SkipEmptyParts);
        if (!params.isEmpty())
        {
            QDateTime dateTime = QDateTime::fromTime_t(params.last().toInt());
            if (dateTime.isValid())
            {
                QDateTime current = QDateTime::currentDateTime();
                int msecs = dateTime.time().msecsTo(current.time());
                if (msecs < 1000)
                    message = QString("PING %1ms").arg(msecs);
                else if (msecs < 60000)
                    message = QString("PING %1s").arg(msecs / 1000.0, 0, 'f', 1);
                else
                    message = QString("PING %1m").arg(msecs / 60000);
            }
        }
    }

    MessageView* view = static_cast<MessageView*>(currentWidget());
    QStringList params;
    params << view->senderHtml(origin) << message;
    view->receiveMessage(tr("! %1 replied CTCP-%2"), params);
}

void SessionTabWidget::on_msgNumericMessageReceived(const QString& origin, uint code, const QStringList& params)
{
    model->numericMessageReceived(code, params);

    MessageView* currentView = static_cast<MessageView*>(currentWidget());
    if (!currentView)
    {
        QString target = sessio->resolveTarget(origin, QString());
        prepareView(target, true, false);
    }
    currentView = static_cast<MessageView*>(currentWidget());
    switch (code)
    {
    case Irc::Rfc::RPL_WELCOME:
        {
            handler.renameView(tabText(0), origin);
            setTabText(0, origin);
            emit titleChanged(origin);
        }
        break;

    case Irc::Rfc::RPL_AWAY:
        {
            QStringList list;
            list << currentView->senderHtml(params.value(1)) << params.value(2);
            currentView->receiveMessage("! %1 %2", list);
        }
        return;

    case Irc::Rfc::RPL_WHOISUSER:
        {
            QStringList list;
            list << currentView->senderHtml(params.value(1)) << params.value(2) << params.value(3) << params.value(4);
            currentView->receiveMessage(tr("! %1 is %2@%3 (%4)"), list);
        }
        return;

    case Irc::Rfc::RPL_WHOISSERVER:
        {
            QStringList list;
            list << currentView->senderHtml(params.value(1)) << params.value(2) << params.value(3);
            currentView->receiveMessage(tr("! %1 online via %2 (%3)"), list);
        }
        return;

    case Irc::Rfc::RPL_WHOISOPERATOR:
        {
            qDebug() << "numeric:" << origin << code << params;
            Q_ASSERT(false);
            //currentView->logMessage(params.value(1), "! %1 is online via %2", QString("%1 (%2)").arg(params.value(2)).arg(params.value(3)));
        }
        return;

    case Irc::Rfc::RPL_WHOWASUSER:
        {
            currentView->receiveMessage(tr("! %1 was %2@%3 %4 %5"), params.mid(1));
        }
        return;

    case Irc::Rfc::RPL_ENDOFWHOWAS:
        {
            // End of /WHOWAS list.
        }
        return;

    case Irc::Rfc::RPL_WHOISIDLE:
        {
            QString sender = currentView->senderHtml(params.value(1));

            QTime idle = QTime().addSecs(params.value(2).toInt());
            currentView->receiveMessage(tr("! %1 has been idle for %2"), QStringList() << sender << idle.toString());

            QDateTime signon = QDateTime::fromTime_t(params.value(3).toInt());
            currentView->receiveMessage(tr("! %1 has been online since %2"), QStringList() << sender << signon.toString());
        }
        return;

    case Irc::Rfc::RPL_ENDOFWHOIS:
        {
            // End of /WHOIS list.
        }
        return;

    case Irc::Rfc::RPL_WHOISCHANNELS:
        {
            QStringList list;
            list << currentView->senderHtml(params.value(1)) << params.value(2);
            currentView->receiveMessage(tr("! %1 is on channels %2"), list);
        }
        return;

    case 320:
        {
            // "is identified to services"
            currentView->receiveMessage("! %1 %2", QStringList() << currentView->senderHtml(params.value(1)) << params.value(2));
        }
        return;

    case Irc::Rfc::RPL_CHANNELMODEIS:
        {
            QString target = sessio->resolveTarget(QString(), params.value(1));
            prepareView(target, true, false);

            handler.view(target)->receiveMessage(tr("! %1 mode is %2"), QStringList() << params.value(1) << params.value(2));
        }
        return;

    case Irc::Rfc::RPL_CHANNELURL:
        {
            QString target = sessio->resolveTarget(QString(), params.value(1));
            prepareView(target, true, false);

            handler.view(target)->receiveMessage(tr("! %1 url is %2"), QStringList() << params.value(1) << params.value(2));
        }
        return;

    case Irc::Rfc::RPL_CHANNELCREATED:
        {
            QString target = sessio->resolveTarget(QString(), params.value(1));
            prepareView(target, true, false);

            QDateTime dateTime = QDateTime::fromTime_t(params.value(2).toInt());
            handler.view(target)->receiveMessage(tr("! %1 was created %2"), QStringList() << params.value(1) << dateTime.toString());
        }
        return;

    case Irc::Rfc::RPL_NOTOPIC:
        {
            QString target = sessio->resolveTarget(QString(), params.value(1));
            prepareView(target, true, false);

            handler.view(target)->receiveMessage(tr("! %1 has no topic set"), QStringList() << params.value(1));
        }
        return;

    case Irc::Rfc::RPL_TOPIC:
        {
            QString target = sessio->resolveTarget(QString(), params.value(1));
            prepareView(target, true, false);

            handler.view(target)->receiveMessage(tr("! %1 topic is \"%2\""), QStringList() << params.value(1) << Irc::Util::messageToHtml(params.value(2)));
        }
        return;

    case Irc::Rfc::RPL_TOPICSET:
        {
            QDateTime dateTime = QDateTime::fromTime_t(params.value(3).toInt());
            if (dateTime.isValid())
            {
                QString target = sessio->resolveTarget(QString(), params.value(1));
                prepareView(target, true, false);

                QStringList list;
                list << params.value(1) << dateTime.toString() << handler.view(target)->senderHtml(params.value(2));
                handler.view(target)->receiveMessage(tr("! %1 topic set %2 by %3"), list);
            }
        }
        return;

    case Irc::Rfc::RPL_INVITING:
        {
            currentView->receiveMessage(tr("! inviting %1 to %2"), QStringList() << params.value(1) << params.value(2));
        }
        return;

    case Irc::Rfc::RPL_VERSION:
        {
            currentView->receiveMessage(tr("! %1 version is %2"), QStringList() << origin << params.value(1));
        }
        return;

    case Irc::Rfc::RPL_NAMREPLY:
        {
            QStringList list = params;
            list.removeAll("=");
            list.removeAll("@");

            QString target = sessio->resolveTarget(QString(), list.value(1));
            prepareView(target, true, false);

            QStringList nicks = list.value(2).split(" ", QString::SkipEmptyParts);
            QStringList nickLinks;
            foreach (const QString& nick, nicks)
                nickLinks << handler.view(target)->senderHtml(nick);
            QString msg = QString("[ %2 ]").arg(nickLinks.join(" ] [ "));
            handler.view(target)->receiveMessage(tr("! %1"), QStringList(msg));
        }
        return;

    case Irc::Rfc::RPL_ENDOFNAMES:
        return;

    case Irc::Rfc::RPL_TIME:
        {
            currentView->receiveMessage(tr("! %1 time is %2"), QStringList() << params.value(1) << params.value(2));
        }
        return;

    default:
        break;
    }

    //qDebug() << "numeric:" << origin << code;
    currentView->receiveMessage(tr("[%1] %2"), QStringList() << QString::number(code) << params.join(" "));
}

void SessionTabWidget::on_msgUnknownMessageReceived(const QString& origin, const QStringList& params)
{
    // TODO
    qWarning() << "unknown:" << origin << params;
}
*/

void SessionTabWidget::send(const QString& message)
{
    /*
    if (message.trimmed().isEmpty())
        return;

    QString receiver = tabText(currentIndex());
    if (message.startsWith('/'))
    {
        engine->setScriptObject("view", handler.view(receiver.toLower()));
        if (!engine->evaluateCommand(message.mid(1), receiver, handler.receivers()))
        {
            // failed
            qDebug("failed to evaluate: %s", qPrintable(message));
        }
    }
    else
    {
        sessio->cmdMessage(receiver, message);
    }
    */
}

void SessionTabWidget::tabActivated(int index)
{
    setTabAlert(index, false);
    setTabHighlight(index, false);
    if (isVisible())
    {
        window()->setWindowFilePath(tabText(index));
        if (currentWidget())
            currentWidget()->setFocus();
    }
}

void SessionTabWidget::delayedTabReset()
{
    delayedIndexes += currentIndex();
    QTimer::singleShot(500, this, SLOT(delayedTabResetTimeout()));
    if (isVisible())
        window()->setWindowFilePath(tabText(currentIndex()));
}

void SessionTabWidget::delayedTabResetTimeout()
{
    if (delayedIndexes.isEmpty())
        return;

    int index = delayedIndexes.takeFirst();
    tabActivated(index);
}

void SessionTabWidget::prepareView(const QString& target, bool create, bool highlight)
{
    /*
    if (create && !handler.contains(target))
        openView(target);

    int index = indexOf(handler.view(target));
    if (highlight && (index != currentIndex() || !isVisible()))
        setTabHighlight(index, true);
    */
}
